Linux程序管理技术

GO


1. 什么是进程(process)

在Linux系统中,触发任何一个事件时,系统都会将它定义为一个进程,并且给予这个进程一个ID,称为PID,同时依据触发这个进程的用户与相关属性关系,给予这个PID一组有效的权限设置。从此以后,这个PID能够在系统上面进行的操作,就与这个PID的权限有关了。

1.1. 进程与程序(process & program)

  • 程序(program):通常为二进制程序放置在存储媒介中(如硬盘、光盘等),以物理文件的形式存在。
  • 进程(process):程序被触发后,执行者的权限与属性、程序的程序代码与所需数据等都会被加载到内存中,操作系统并给予这个内存内的单元一个标识符(PID),可以说,进程就是一个正在运行中的程序。
  • 父进程与子进程:举例说明,原本运行中的bash就是父进程,被父进程触发的另一个bash就是子进程。
  • 过程调用的流程:在Linux的过程调用中通常称为fork-and-exec的流程。进程都会通过父进程以复制(fork)的方式产生一个一模一样的子进程,然后被复制出来的子进程再以exec的方式来执行实际要进行的进程,最终就成为一子进程的存在。
  • 服务:常驻内存的进程称为服务(daemon)。

1.2. Linux的多用户、多任务环境

  • 多用户:系统中有一个root用户具有所有的权限。其它的用户都有各自的UID。多个用户可同时的登陆到系统上进行工作。
  • 多任务:由于Linux的特性,运行多个任务同时进行(属于CPU调度)。
  • 多重登陆:用一个用户可重复登陆多个终端,方便管理工作。
  • 特殊的进程管理行为:进程管理和工作管理,能够很好的为管理人员提供很方便的管理方式。

2. 工作管理(job control)

这个管理工作(job control)是用在bash环境下的,也就是说:当我们登陆系统取得bash shell之后,在单一终端下同时进行多个工作的行为管理。

2.1. 什么是工作管理

在进行工作管理的行为中,其实每个工作都是目前bash的子进程,即彼此之间是有相关性的。我们无法以 job control 的方式由 tty1 的环境去管理 tty2 的bash。

我们可以在/etc/security/limits.conf里面设置用户同时可以登陆的连接数,在这样的情况下,某些用户可能仅能以一个连接来工作!

由于假设我们只有一个终端,因此在可以出现提示符让你操作的环境就称为前台(foreground),至于其它工作就可以让你放入后台(background)去暂停或运行。要注意的是,放入后台的工作想要运行时,它必须不能够与用户互动。而且放入后台的工作是不能使用Ctrl+C的方式来终止的。

总之,要进行 bash 的 job control 必须要注意到的限制是:

  • 这些工作所触发的进程必须来自于你shell的子进程(只管理自己的bash)。
  • 前台:你可以控制与执行命令的这个环境称为前台(foreground)的工作。
  • 后台:可以自行运行的工作,你无法使用Ctrl+C来终止它,可使用bg/fg调用该工作。
  • 后台中执行的进程不能等待terminal/shell的输入(input)

2.2. job control的管理

bash只能够管理自己的工作而不能管理其它bash的工作,此外,又分为前台和后台,而后台里面的工作状态又可以分为暂停(stop)运行中(running)。实际进行的job控制的命令如下:

2.2.1. & 直接将命令丢到后台中执行

使用方式:conmmand &

一个例子:将/etc/整个备份成为/tmp/etc.tar.gz且不要等待,可以这样做:

1
2
3
[root@AmingLinux-105 ~]# tar -zpcf /tmp/etc.tar.gz /etc &
[1] 1693
[root@AmingLinux-105 ~]# tar: Removing leading `/' from member names

说明:

  • 在括号内的号码为工作号码(job number),该号码与bash的控制有关。但它既然是个命令触发的,所以当然一定是一个进程,因此还会查看到有 job number 也搭配一个PID。
  • 后续的 1693 则是这个工作在系统中的 PID。至于后续出现的数据是 tar 执行的数据流。
  • 由于我们没有加上数据流重定向,所以会影响界面,但是却不会影响前台的操作。

当这个后台运行的程序完成的时候,会出现类似下面的信息:

1
[1]+ 完成 tar -zpcf /tmp/etc.tar.gz /etc

为了防止这个后台运行的程序的数据流会出现在屏幕上,妨碍我们的继续的操作,可以这样做,就是利用重定向将数据流重定向到文件,如下:

1
2
[root@AmingLinux-105 ~]# tar -zpcvf /tmp/etc.tar.gz /etc > /tmp/log.txt 2>&1 &
[1] 1943

如此一来,输出的信息都传送到/tmp/log.txt当中,就不会影响到前台的工作了。

2.2.2. Ctrl+Z 将目前的工作丢到后台中暂停

使用Ctrl+Z可以把当前工作的进程暂停掉并将它丢到后台,这样我们又取得了前台的操控权。示例如下:

1
2
3
4
5
6
7
8
[root@AmingLinux-105 ~]# vim ~/.bashrc
# 在Vim的一般模式下,按下 Ctrl+Z 这两个键
[1]+ Stopped vim ~/.bashrc
[root@AmingLinux-105 ~]# <==顺利取得了前台的控制权
[root@AmingLinux-105 ~]# sleep 100
^Z <==继续按 Ctrl+Z 暂停这个工作
[2]+ Stopped sleep 100
[root@AmingLinux-105 ~]#

说明:

  • 当按下 Ctrl+Z 后,屏幕上会出现[1],表示这是第一个工作。
  • 那个+代表最近一个被丢进后台的工作,且目前在后台下默认会被取用的那个工作(与fg这个命令有关)。
  • Stopped则代表目前这个工作的状态,在默认的情况下,使用Ctrl+Z丢到后台当中的工作都是“暂停”的状态。

2.2.3. Ctrl+C 将目前的工作终止掉

使用Ctrl+C可以终止正在前台运行的进程,它并不能终止后台暂停或后台运行的进程。

2.2.4. job 查看目前的后台工作状态

jobs的使用方法:jobs [-lrs]

选项 意义
-l 除了列出 job number 与命令串之外,同时列出 PID 的号码
-r 仅列出正在后台 run 的工作
-s 仅列出正在后台当中暂停(stop)的工作

范例:查看目前的bash当中,所有的工作,与对应的PID:

1
2
3
[root@AmingLinux-105 ~]# jobs -l
[1]- 1949 停止 vim ~/.bashrc
[2]+ 1950 停止 sleep 100

说明:

  • 一般来说,直接执行jobs即可
  • 若是想要知道该 job number 的PID号码,可以加上-l这个参数
  • 在输出信息中,那个+代表默认的取用工作,如果我目前有两个工作在后台当中,两个工作都是暂停的,而如果我仅输入fg时,那么哪个[2]所代表的工作会被拿到前台当中来处理
  • 其实+代表最近被放到后台的工作号码,-代表最近最后第二个被放置到后台中的工作号码。而如果超过最后三个以后的工作,就不会有+/-

2.2.5. fg 将后台工作拿到前台来处理

使用方法:fg %jobnumber

  • %jobnumber : jobnumber为工作号码(数字),注意,那个%是可有可无的。

范例:先以 jobs 查看工作,再将工作取出:

1
2
3
4
5
6
7
8
9
10
[root@AmingLinux-105 ~]# jobs
[1]- 已停止 vim ~/.bashrc
[2]+ 已停止 sleep 100
[root@AmingLinux-105 ~]# fg
sleep 100 <==再按下Ctrl+Z
[root@AmingLinux-105 ~]# fg %1
vim ~/.bashrc
<==再按下Ctrl+Z
[1]+ 已停止 vim ~/.bashrc
[root@AmingLinux-105 ~]#

2.2.6. bg 让工作在后台下的状态变成运行中

使用方法:bg %jobnumber

  • %jobnumber : jobnumber为工作号码(数字),注意,那个%是可有可无的。

使用这个命令后,再用jobs查看后台进程就会发现后台的进程的状态变为Running了。

2.2.7. kill 管理后台当中的工作

可以用 kill 工具来管理后台当中的工作。用法:kill -signal %jobnumber

通常我们在管理工作的用法是这样的:kill -9 %jobnumber

说明:

  • -9的意思是强制删除一个不正常的工作。
  • kill后面接的数字默认是PID,如果想要管理bash的工作控制,就得加上%数字了。
  • kill的详细用法参考下面的进程管理章节。

2.3. 脱机管理问题

其实,我们在工作管理当中提到的“后台”指的是在终端机模式下可以避免Ctrl+C中断的一个情境,并不是放到系统的后台去。所以,工作管理的后台依旧与终端机有关。在这样的情况下,如果是以远程连接的方式连接到Linux主机,并且将工作以&的方式放到后台去,这样,在工作尚未结束的情况下脱机了,该工作就会被中断掉。

那如何解决这个问题?如果我的工作需要进行一大段时间,我又不能防止在后台下面,那该如何处理?

首先,我们可以利用at等工具来处理即可,因为at是将工作放置到系统后台,而与终端机无关。

另外,可以尝试使用nohup这个命令来处理。这个nohup可以让你在脱机或注销系统后,还能够让工作继续进行。它的语法如下:

  • nohup [命令与参数] 在终端机前台中工作
  • nohup [命令与参数] & 在终端机后台中工作

上面需要注意的是,nohup并不支持bash内置的命令,因此你的命令必须要是外部命令才行。下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
1. 先编辑一个会“睡着500秒”的程序:
[root@theshuhost ~]# vim sleep500.sh
#!/bin/bash
/bin/sleep 500s
/bin/echo "I have slept 500 seconds."
2. 丢到后台去执行,并且立刻注销系统:
[root@theshuhost ~]# chmod a+x sleep500.sh
[root@theshuhost ~]# nohup ./sleep500.sh &
[1] 4701
[root@theshuhost ~]# nohup: ignoring input and appending output to ‘nohup.out’
[root@theshuhost ~]# exit

如果你再次登陆系统的话,再使用ps查看你的进程,会发现sleep500.sh还在执行中,并不会被中断掉。由于我们的进程最后会输出一个信息,但是nuhup与终端机其实无关了,因此这个信息的输出就会被定向“~/nohup.out”,所以你才会看到上述命令中,当你输入nohup后,会出现那个提示信息。

如果你想要让在后台的工作在你注销后还能够继续执行吗,那么使用nuhup搭配&是不错的运行情境。


3. 进程管理

进程管理非常的重要,这是因为:

  • 首先,我们在操作系统中的各项工作其实都是经过某个PID来达成的(包括你的bash环境),因此,能不能进行某项工作就与该进程的权限有关了。
  • 再来,如果你的Linux系统是个很忙碌的系统,那么当整个系统资源快要被使用光时,你是否能够找出最耗系统的那个进程,然后删除该进程,让系统恢复正常呢?
  • 此外,如果由于某个程序写的不好,导致产生一个有问题的进程在内存当中,你又该如何找出它,然后将它删除?
  • 如果同时有五六项工作在你的系统当中运行,但其中有一项工作才是最重要的,该如何让那一项重要的工作被最优先执行呢?

所以说,一个称职的系统管理员,必须要熟悉进程的管理流程才行,否则当系统发生问题时,还真是很难解决问题呢。下面的内容会先介绍如何查看程序与程序的状态,然后再加以过程控制。

3.1. 进程的查看

3.1.1. ps 静态查看系统进程

作为系统管理员,一定要知道您所管理的系统都有哪些进程在运行,除了上面所介绍的top可以简单查看之外,还有一个专门显示系统进程的命令,就是ps命令。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.2 24324 5444 ? Ss 09:02 0:07 /sbin/init spla
root 2 0.0 0.0 0 0 ? S 09:02 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? S 09:02 0:00 [ksoftirqd/0]
root 5 0.0 0.0 0 0 ? S< 09:02 0:00 [kworker/0:0H]
root 7 0.0 0.0 0 0 ? S 09:02 0:11 [rcu_sched]
root 8 0.0 0.0 0 0 ? S 09:02 0:00 [rcu_bh]
root 9 0.0 0.0 0 0 ? S 09:02 0:00 [migration/0]
root 10 0.0 0.0 0 0 ? S 09:02 0:00 [watchdog/0]
root 11 0.0 0.0 0 0 ? S 09:02 0:00 [watchdog/1]
root 12 0.0 0.0 0 0 ? S 09:02 0:00 [migration/1]
root 13 0.0 0.0 0 0 ? S 09:02 0:00 [ksoftirqd/1]
root 15 0.0 0.0 0 0 ? S< 09:02 0:00 [kworker/1:0H]
root 16 0.0 0.0 0 0 ? S 09:02 0:00 [kdevtmpfs]
root 17 0.0 0.0 0 0 ? S< 09:02 0:00 [netns]

ps aux命令内容说明:

  • USER、PID、%CPU、%MEM、VSZ虚拟内存使用大小、RSS内存的使用量、TTY、STAT状态、TIME进程运行时间、COMMAAND命令
    • USER:该进程属于哪个用户账号
    • PID:该进程的进程标识符
    • %CPU:该进程使用掉的CPU资源百分比
    • %MEM:该进程所占用的物理内存百分比
    • VSZ:该进程使用掉的虚拟内存两(kb)
    • RSS:该进程占用的固定的内存量(kb)
    • TTY:该进程是在哪个终端机上面运行,若如终端机无关则显示?,另外,tty1~tty6是本机上面的登陆者程序,若为pts/0等的,则表示为由网络连接进主机的进程
    • STAT:该进程目前的状态,状态显示与ps -l的S标识相同(R/S/T/Z)
    • START:该进程被触发启动的时间
    • TIME:该进程实际使用CPU运行的时间
    • COMMAND:该进程的实际命令
  • 关于STAT状态的说明
    • S:休眠sleep
    • s:父进程(主进程)
    • <:高优先级
    • N:低优先级
    • +:前台
    • R:正在运行的
    • l(小写的L):多线程
    • L:lock锁
    • X:已经死掉的进程
    • Z:僵尸进程(即不能杀死的进程,只能通过重启才能去掉)
    • T:被暂停的
    • D:不能中断的
    • W:这个好像是说,从内核2.6xx以后,表示为没有足够的内存页分配

另外一个类似与ps aux的使用方式为ps -elf,两者的功能差不多。其选项说明如下:

  • -l:把当前终端下运行的程序列出来
  • -e:把后台所有的程序都列出来
  • -f:显示Stime

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ps -elf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 1 0 0 80 0 - 6327 - 09:41 ? 00:00:07 /sbin
1 S root 2 0 0 80 0 - 0 - 09:41 ? 00:00:00 [kthr
1 S root 3 2 0 80 0 - 0 - 09:41 ? 00:00:00 [ksof
1 S root 5 2 0 60 -20 - 0 - 09:41 ? 00:00:00 [kwor
1 S root 7 2 0 80 0 - 0 - 09:41 ? 00:00:08 [rcu_
1 S root 8 2 0 80 0 - 0 - 09:41 ? 00:00:00 [rcu_
1 S root 9 2 0 -40 - - 0 - 09:41 ? 00:00:00 [migr
5 S root 10 2 0 -40 - - 0 - 09:41 ? 00:00:00 [watc
5 S root 11 2 0 -40 - - 0 - 09:41 ? 00:00:00 [watc
1 S root 12 2 0 -40 - - 0 - 09:41 ? 00:00:00 [migr
1 S root 13 2 0 80 0 - 0 - 09:41 ? 00:00:00 [ksof
1 S root 15 2 0 60 -20 - 0 - 09:41 ? 00:00:00 [kwor
.......

ps -elf命令内容说明:系统整体的进程运行是非常多的,但如果使用ps -l则仅列出与你的操作环境(bash)有关的进程而已,即最上层的父进程会是你自己的bash而没有扩展到init这个进程去,那么ps -elf显示出的数据都是什么意思?如下:

  • F:代表这个进程标志(process flags),说明这个进程的权限,常见号码有:
    • 若为4表示次进程的权限为root
    • 若为1则表示此子进程仅可进行复制(fork)而无法实际执行(exec)
  • S:代表这个进程的状态(STAT),主要的状态有:
    • R(Running):该进程正在运行中
    • S(Sleep):该进程目前正在睡眠状态(idle),但可以被唤醒(signal)
    • D:不可被唤醒的睡眠状态,通常这个进程可能在等待I/O的情况(ex>打印)
    • T:停止状态(stop),可能是在工作控制(后台暂停)或除错(traced)状态
    • Z(Zombie):僵尸状态,进程已经终止但却无法被删除至内存外
  • UID/PID/PPID:代表此进程被该UID所拥有/进程的PID号码/次进程的父进程PID号码
  • C:代表CPU的使用率,单位为百分比
    PRI/NI:Priority/Nice的缩写,代表次进程被CPU所执行的优先级,数值越小代表该进程越快被CPU执行。详细的PRI与NI在下一小节说明
  • ADDR/SZ/WCHAN:都与内存有关,ADDR是kernel function,指出该进程在内存的哪个部分,如果是个running的进程,一般就会显示“-”。SZ代表此进程用掉多少内存/WCHAN表示目前进程是否运行中,同样,若为“-”表示正在运行中
  • STIME:运行该进程时系统的时间
  • TTY:登陆者的终端机位置,若为远程登陆则使用动态终端接口(pts/0)
  • TIME:使用掉的CPU时间,注意,是此进程实际花费CPU运行的时间,而不是系统时间
  • CMD:就是command的缩写,造成次程序的触发进程的命令是什么

关于ps命令,以上两个用法就足够了。在工作中,经常把ps命令和管道父结合使用,用来查看某个进程或者它的数量,如下所示:

1
2
3
4
# ps aux | grep -c mingetty
1
# ps aux | grep mingetty
theshu 4454 0.0 0.0 6864 812 pts/1 S+ 12:16 0:00 grep --color=auto mingetty

3.1.2. pstree 程序树查看程序之间的关系

若是没有这个命令,则用这个命令来安装它:yum install -y psmisc

pstree的用法如下:
pstree [-A|U] [-up]

参数 意义
-A 各进程树之间的连接以 ASCII 字符来连接
-U 各进程树之间的连接以 utf8 码的字符来连接,在某些终端接口下可能会有错误
-p 同时列出每个进程的PID
-u 同时列出每个进程的所属账号名称

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
[theshu@theshuhost ~]$ pstree
systemd─┬─AliYunDun───14*[{AliYunDun}]
├─AliYunDunUpdate───3*[{AliYunDunUpdate}]
├─agetty
├─aliyun-service───6*[{aliyun-service}]
├─atd
├─auditd───{auditd}
├─crond
├─dbus-daemon
├─dhclient
├─ntpd
├─polkitd───5*[{polkitd}]
├─rsyslogd───2*[{rsyslogd}]
├─sshd───sshd───sshd───bash───pstree
├─systemd-journal
├─systemd-logind
├─systemd-udevd
└─tuned───4*[{tuned}]
[theshu@theshuhost ~]$ pstree -up
systemd(1)─┬─AliYunDun(1148)─┬─{AliYunDun}(1149)
│ ├─{AliYunDun}(1150)
│ ├─{AliYunDun}(1396)
│ ├─{AliYunDun}(1397)
│ ├─{AliYunDun}(1398)
│ ├─{AliYunDun}(1399)
│ ├─{AliYunDun}(1400)
│ ├─{AliYunDun}(1448)
│ ├─{AliYunDun}(1464)
│ ├─{AliYunDun}(1465)
│ ├─{AliYunDun}(1466)
│ ├─{AliYunDun}(1467)
│ ├─{AliYunDun}(1468)
│ └─{AliYunDun}(31019)
├─AliYunDunUpdate(891)─┬─{AliYunDunUpdate}(894)
│ ├─{AliYunDunUpdate}(895)
│ └─{AliYunDunUpdate}(915)
├─agetty(879)
├─aliyun-service(7725)─┬─{aliyun-service}(7728)
│ ├─{aliyun-service}(7729)
│ ├─{aliyun-service}(7730)
│ ├─{aliyun-service}(7731)
│ ├─{aliyun-service}(7732)
│ └─{aliyun-service}(7733)
├─atd(484)
├─auditd(440)───{auditd}(441)
├─crond(485)
├─dbus-daemon(467,dbus)
├─dhclient(752)
├─ntpd(833,ntp)
├─polkitd(466,polkitd)─┬─{polkitd}(487)
│ ├─{polkitd}(488)
│ ├─{polkitd}(496)
│ ├─{polkitd}(499)
│ └─{polkitd}(501)
├─rsyslogd(477)─┬─{rsyslogd}(494)
│ └─{rsyslogd}(497)
├─sshd(1536)───sshd(5322)───sshd(5324,theshu)───bash(5325)───pstree(+
├─systemd-journal(326)
├─systemd-logind(463)
├─systemd-udevd(344)
└─tuned(815)─┬─{tuned}(1450)
├─{tuned}(1451)
├─{tuned}(1452)
└─{tuned}(1458)

3.1.3. top 动态查看系统进程

top的使用方式:top [-d 数字]top [-bnp]

参数:

参数 意义
-d 后面可以接秒数,就是整个进程界面更新的秒数,默认是3秒
-b 以批次的方式执行top,还有更多的参数可以使用。
通常会搭配数据流重定向来将批处理的结果输出成为文件
-n 与-b搭配,意义是,需要进行几次 top 的输出结果
-p 指定某些个PID来进行查看检测而已

在top执行过程中可以使用的按键命令:

按键 意义
? 显示在top当中可以输入的按键命令
P 以CPU的使用资源排序显示
M 以内存的使用资源排序显示
N 以PID来排序
T 由该进程使用的CPU时间积累(TIME+)排序
k 给予某个PID一个信号(signal)
r 给予某个PID重新制定一个nice值
q 离开top软件的按键
大于 向下翻看
小于 向上翻看

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# top
top - 17:37:05 up 2:25, 1 user, load average: 0.00, 0.01, 0.05
Tasks: 91 total, 1 running, 90 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1008176 total, 507628 free, 135092 used, 365456 buff/cache
KiB Swap: 4194300 total, 4194300 free, 0 used. 684136 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 128164 6848 4076 S 0.0 0.7 0:01.38 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.02 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:02.17 ksoftirqd/0
5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H
7 root rt 0 0 0 0 S 0.0 0.0 0:00.10 migration/0
8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_bh
9 root 20 0 0 0 0 S 0.0 0.0 0:01.30 rcu_sched
10 root rt 0 0 0 0 S 0.0 0.0 0:00.15 watchdog/0
11 root rt 0 0 0 0 S 0.0 0.0 0:00.18 watchdog/1
12 root rt 0 0 0 0 S 0.0 0.0 0:00.06 migration/1
13 root 20 0 0 0 0 S 0.0 0.0 0:00.15 ksoftirqd/1
15 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/1:0H
17 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kdevtmpfs

top命令用于动态监控进程所占的系统资源,每隔5秒变一次。它的特点是把占用系统资源(CPU、内存、磁盘I/O等)最高的进程放到最前面。

如例所示,top命令打印出很多信息,包括系统负载(load average)、进程数(Tasks)、CPU使用情况、内存使用情况以及交换分区使用情况。这些内容其实可以通过其它命令来查看,用top的重点查看的还是下面的进程使用系统资源的详细状况,其中需要关注的是%CPU、%MEM和COMMAND这几项所代表的意义。RES这一项为进程所占用的内存大小,而%MEM这一项为使用内存的百分比。在top状态下,默认按CPU的使用百分比情况排序,按Shift+M可以按照内存使用大小来排序。按数字1可以列出所有核CPU的使用状态,按q可以推出top。

top的内容说明:

  • 第一行(top,综述):同w命令的第一行(或uptime命令)所示的一样,只不过是动态的(默认3s刷新一次状态)。这一行显示的信息分别为:
    • 目前的时间,即是 17:37:05 内容
    • 开机到目前位置所经过的时间,即是 up 2:25 内容
    • 已经登陆系统的用户人数,即是1 user内容
    • 系统在1、5、15分钟的平均工作负载,意思是在1、5、15分钟系统平均要负责运行几个进程(工作)的意思。越小代表系统越闲置,若高于1得要注意你的系统压力是否太过繁复了。
  • 第二行(Tasks,进程):显示的是目前进程的总量与个别进程在什么状态(running,sleeping,stopped,zombie)。比较需要注意的是最后的zombie那个数值,如果不是0,好好看看到底是哪个process变成僵尸了吧。这一行显示的信息分别为:
    • 进程总数 91 total
    • 正在运行进程数 1 running
    • 休眠进程数 90 sleeping
    • 被停止进程数 0 stopped
    • 僵尸进程数 0 zombie
  • 第三行(%Cpus):显示的是CPU的整体负载,每个选项可使用?查阅。需要特别注意的是%wa,那个选项代表的是I/O wait,通常你的系统变慢都是I/O产生的问题比较大。因此这里得要注意这个选项耗用CPU的资源。另外,如果是多内核的设备,可以按下数字键“1”来切换成不同CPU的负载率。这一行的内容分别为:
    • %us 用户态进程,用户空间占用CPU百分比
    • %sy 内核态进程,内核空间占用CPU百分比
    • %ni 用户进程空间内改变过优先级的进程占用CPU百分比
    • %id 空闲CPU百分比
    • %wa等待(I/O)等待输入输出的CPU时间百分比
    • %hi
    • %si
    • %st
  • 第四行(内存):总大小、已使用的、空闲的、缓冲量大小
  • 第五行(Swap):总大小、已使用的、空闲的、缓存量大小
    • 要注意的是,swap的使用量要尽量少,如果swap被大量使用,表示系统的物理内存实在不足。
    • 缓冲和缓存的区别:
      • 缓冲(buffer):是为了提高内存和硬盘(或其他I/O设备)之间的数据交换的速度而设计的(内存–硬盘)
      • 缓存(cache):是为了提高CPU和内存之间的数据交换速度而设计的(内存–CPU)
  • 第六行:这个是当在top进程当中输入命令时显示状态的地方。这一行的内容为:
    • PID:进程ID,PPID:父进程ID
    • RUSER:Real user name
    • USER:进程使用者的用户名
    • PR优先级
    • NI优先值(Nice值,从-20到19,数值越低,优先级越高)
    • VIRT:虚拟内存大小
    • RES:真正内存使用大小
    • SHR:共享内存大小
    • S:表示状态
    • %CPU:上次更新到现在的CPU时间占用百分比
    • %MEM:进程使用的物理内存百分比
    • TIME+:进程使用的CPU时间总计,单位1/100秒
    • COMMAND:命令

在日常工作中常用到命令 top -bn1,它表示非动态打印系统资源的使用情况,和top命令的唯一区别就是,它一次性输出所有信息而非动态显示。这条命令可以用在shell脚本中。示例如下:

1
2
3
4
5
6
7
8
9
10
11
]# top -bn1 | head
top - 17:42:27 up 2:31, 1 user, load average: 0.00, 0.01, 0.05
Tasks: 92 total, 1 running, 91 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1008176 total, 508100 free, 134564 used, 365512 buff/cache
KiB Swap: 4194300 total, 4194300 free, 0 used. 684652 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 128164 6848 4076 S 0.0 0.7 0:01.38 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.02 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:02.17 ksoftirqd/0

top -c 这个命令是使 COMMAND那一列显示的更全一些。

例子:指定PID查看指定的进程查看。如下:

1
2
3
4
5
6
7
8
9
10
11
12
我们自己的bash PID可由 $$ 变量取得,请使用 top 持续查看该 PID:
[root@theshuhost ~]# echo $$
5683
[root@theshuhost ~]# top -d 2 -p 5683
top - 21:00:06 up 24 days, 23:51, 2 users, load average: 0.00, 0.01, 0.05
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.5 sy, 0.0 ni, 99.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1016164 total, 299240 free, 80928 used, 635996 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 771796 avail Mem
---
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5683 root 20 0 115392 1984 1600 S 0.0 0.2 0:00.00 bash

例子:修改NI数值来达到调控进程的目的。如下:

承上题,上面的NI值是0,想要改成10该怎么版呢?在上例的top界面中直接按下r之后,会出现如下的图样:

1
2
3
4
5
6
7
8
top - 21:03:57 up 24 days, 23:55, 2 users, load average: 0.00, 0.01, 0.05
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1016164 total, 301492 free, 78736 used, 635936 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 774072 avail Mem
PID to renice [default pid = 5683] <<==按下r后在这里输入PID号码
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5683 root 20 0 115392 2028 1636 S 0.0 0.2 0:00.00 bash

完成上面的操作后,在状态栏会出现如下的信息:

1
2
3
Renice PID 5683 to value 10 <<==这是nice值
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5683 root 20 0 115392 2028 1636 S 0.0 0.2 0:00.00 bash

接下来你就会看到如下的显示界面(注意,NI值已经发生改变):

1
2
3
4
5
6
7
8
top - 21:06:56 up 24 days, 23:58, 2 users, load average: 0.00, 0.01, 0.05
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1016164 total, 301364 free, 78864 used, 635936 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 773944 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5683 root 30 10 115392 2028 1636 S 0.0 0.2 0:00.00 bash

3.2. 进程的管理

3.2.1. 信号

进程之间是可以相互控制的。那么程序是如何相互管理的呢?其实是通过给予该进程一个信号(signal)去告知该进程你想要让它做什么。因此这个信号就很重要了。

要给予某个已经存在后台中的工作某些操作时,直接给予一个信号给该工作号码即可。那到底有多少signal呢?可以使用kill -l(小写的L)man 7 signal都可以查询到。

主要的信号代号与名称对应及内容如下表所示:

代号 名称 内容
1 SIGHUP  启动被终止的进程,可让该PID重新读取自己的配置文件,类似重新启动
2 SIGINT 相当于用键盘输入Ctrl+C来中断一个进程的进行
9 SIGKILL 代表强制中断一个进程的进行,如果该进程进行到一半,那么尚未完成的部分可能会有“半成品”产生,类似vim会有.filename.swp保留下来
15 SIGTERM 以正常的结束来终止该进程。由于是正常的终止,所以后续的操作会将它完成。不过,如果该进程已经发生问题,就是无法使用正常的方法终止时,输入这个signal也是没用的
17 SIGSTOP 相当于用键盘输入Ctrl+Z来暂停一个进程的进行。

上表中的1、9、15一定要记下来。那么我们如何传送一个信号给某个进程呢?就是通过killkillall来实现。

3.2.2. kill

用法:kill -signal PID

说明:

  • kill可以帮我们将这个signal传送给某个工作(%jobnumber)或者是某个PID(直接输入数字)。kill后面接的数字默认会是PID,所以想要管理bash的工作控制,就要加上%数字了。
  • 强调:kill后面直接加数字与加上%number的情况是不同的。这个很重要。一定要记得那个%是专门用在工作控制的。
  • signal除了以数值来表示之外,也可以使用信号名称。

接下来我们就活用一下kill与刚才上面提到的ps来做个简单的练习吧:

  • 题目:以ps找出syslog这个进程的PID后,再使用kill传递信号,使得syslog可以重新读取配置文件。
    • 由于需要重新读取配置文件,因此signal是1号。至于找出syslog的PID可以这样做:ps aux | grep 'syslog' | grep -v 'grep' | awk '{print $2}'
    • 接下来则是实际使用kill -1 PID,因此,整串命令会是这样:kill -SIGHUP $(ps aux | grep 'syslog' | grep -v 'grep' | awk '{print $2}')
    • 如果要确认有没有重新启动syslog,可以参考日志文件的内容,使用如下命令查阅:tail -5 /var/log/messages。如果有看到类似“www syslogd 1.4.1: restart”之类的字样,就是表示 syslogd已经重启了。

了解了这个用法以后,如果将来你要想将某个莫名其妙的登陆者的连接删除的话,就可以通过使用pstree -p找到相关进程,然后再以kill -9将该进程删除,该连接就会被踢掉了。

3.2.3. killall

用法:killall -signal 命令名称killall [-iIe] [command name]

参数 意义
-i interactive的意思,交互式的,若需要删除时,会出现提示符给用户
-e exact的意思,表示后面接的 command name 要一致,但整个完整的命令不能超过15个字符
-I 命令名称(可能含参数)忽略大小写

说明:

  • 由于kill命令后面必须要加上PID(或者是job number),所以,通常kill都会配合ps,pstree等命令,因为我们必须要找到对应的那个进程的ID。
  • 而killall命令可以利用“执行命令的名称”来给予信号。所以很方便。

示例如下:

1
2
3
4
5
6
示例一:给予syslogd这个命令启动的PID一个 SIGHUP 的信号
# killall -1 syslogd
示例二:强制终止所有以 httpd 启动的进程
# killall -9 httpd
示例三:依次询问每个bash进程是否需要被终止运行
# killall -i -9 bash

3.2.4. 进程管理的总结

  • 要删除某个进程,我们可以使用PID或者是启动该进程的命令名称。
  • 如果要删除某个服务,最简单的方法就是利用killall,因为它可以将系统当中所有以命令名称启动的进程全部删除。

3.3. 关于进程的执行顺序

Linux是多用户、多任务的系统,由top命令的输出结果我们也发现,系统同时间有非常多的进程在运行中,只是绝大部分的进程都在休眠(sleeping)状态而已。试想一下,如果所有的进程同时被唤醒,那么CPU应该要先处理哪个进程呢?也就是说,哪个进程被执行的优选序比较高?这就得考虑到程序的优先执行序(Priority)与CPU调度。

CPU调度与例行性工作调度(也就是计划任务)不一样,CPU调度指的是每个程序被CPU运行的演算规则,而例行性工作调度则是将某个程序安排在某个时间再交由系统执行。CPU调度与操作系统较具有相关性。

3.3.1. Priority 与 Nice值

有时Linux系统下的任务需要分清楚什么进程任务比较重要,什么可以暂缓执行,优先执行什么。为了达到这个目的,Linux给予进程一个所谓的优先执行序(Priority,PRI),这个PRI值越低代表越优先的意思。不过这个PRI值是由内核动态调整的,用户是无法直接调整PRI值的。可以用ps等命令来查看到PRI值。(ps -l

因为PRI值我们无法直接调整,那么我们想要调整进程的优先序时,就得要通过Nice值了。Nice值一般都在PRI值的旁边,ps等命令的界面中的NI值。

一般来说,PRI与NI的相关性如下:PRI (new) = PRI(old) + NICE

说明:

  • 注意,如果原本的PRI是50,并不是我们给予一个Nice=5,就会让PRI变成55。因为PRI是系统“动态”决定的,所以,虽然Nice值可以影响PRI,不过最终的PRI仍然要经过系统分析后才会决定的。另外,Nice值是有正负的,而既然PRI越小越早被执行,所以,当Nice值为负时,那么该进程就会降低PRI值,即会变得较优先被处理。
  • Nice值可以调整的范围是 -20——19
  • root可随意调整自己或他人进程的Nice值,且范围是 -20——19
  • 一般用户仅可调整自己进程的Nice值,且范围仅为 0——19(避免一般用户抢占系统资源)
  • 一般用户仅可将Nice值越调越高,例如本来Nice为5,则将来仅能调整到大于5

综上所述,要调整某个进程的优先执行序,就是调整该进程的Nice值。那么如何给予某个进程Nice值呢?有以下两种方式:

  1. 一开始执行程序就立即给予一个特定的Nice值:用nice命令实现。
  2. 调整某个已经存在的PID的Nice值:用renice命令实现。

3.3.2. nice 新执行的命令即给予新的nice值

使用方法:nice [-n 数字] command

参数:

  • -n:后面接一个数值,数值的范围是-20到19

例子:用root给一个Nice值为-5,用于执行vi,并查看该进程,最后退出它:

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@theshuhost ~]# nice -n -5 vi &
[1] 7142
[root@theshuhost ~]# ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 7123 7102 0 80 0 - 49420 poll_s pts/0 00:00:00 sudo
4 S 0 7124 7123 0 80 0 - 47402 do_wai pts/0 00:00:00 su
4 S 0 7125 7124 0 80 0 - 28848 do_wai pts/0 00:00:00 bash
4 T 0 7142 7125 0 75 -5 - 31002 do_sig pts/0 00:00:00 vi
0 R 0 7143 7125 0 80 0 - 37235 - pts/0 00:00:00 ps
# 原本的bash PRI为80,所以vi默认应为80,不过由于给予Nice值为-5,
# 因此vi的PRI降低了。但有时候做这个实验你会看,并非会降低到75,因为内核还会动态调整。
[root@theshuhost ~]# kill -9 %1 <==测试完毕将vi关闭
[1]+ Stopped nice -n -5 vi

通常什么时候需要将Nice值调大呢?举例来说,系统的后台工作中,某些比较不重要的进程在运行,例如备份工作,由于备份工作相当消耗系统资源,这个时候就可以将备份的命令的Nice值调大一些,可以使系统的资源分配得更为公平。

3.3.3. renice 已存在进程的nice重新调整

使用方法:renice [number] PID

例子:找出自己的bash的PID,并将该PID的Nice调整到10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@theshuhost ~]# ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 7123 7102 0 80 0 - 49420 poll_s pts/0 00:00:00 sudo
4 S 0 7124 7123 0 80 0 - 47402 do_wai pts/0 00:00:00 su
4 S 0 7125 7124 0 80 0 - 28848 do_wai pts/0 00:00:00 bash
0 R 0 7154 7125 0 80 0 - 37235 - pts/0 00:00:00 ps
[root@theshuhost ~]# renice 10 7125
7125 (process ID) old priority 0, new priority 10
[root@theshuhost ~]# ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 7123 7102 0 80 0 - 49420 poll_s pts/0 00:00:00 sudo
4 S 0 7124 7123 0 80 0 - 47402 do_wai pts/0 00:00:00 su
4 S 0 7125 7124 0 90 10 - 28848 do_wai pts/0 00:00:00 bash
0 R 0 7156 7125 0 90 10 - 37235 - pts/0 00:00:00 ps

说明:

  • 如果要调整的是已经存在的某个进程的话,那么就得要使用renice这个命令了。使用的方法很简单。renice后面接上数值和PID即可。因为后面接的是PID,所以要使用ps等命令来找出这个进程的PID才行。
  • 由上面的例子我们也可以看得出来,虽然修改的是bash这个进程,但是该进程所触发的ps命令当中的Nice也会继承而为10。整个Nice值是可以在父进程->子进程之间传递呢。
  • 另外,除了renice之外,其实哪个top也同样可以调整Nice值的。

3.4. 系统资源的查看

除了系统的进程之外,我们还必须就系统的一些资源进行检查。如以下的这些命令。

3.4.1. free 查看内存使用情况

示例如下:

1
2
3
4
# free
total used free shared buff/cache available
Mem: 2052900 366112 605324 209940 1081464 1245724
Swap: 2085884 0 2085884

只需要敲一个free然后回车就可以知道当前系统的总内存大小以及使用内存的情况。另外我们还可以加-m或者-g选项分别以M或G为单位打印内存使用状况(默认单位是kb)。如下所示:

1
2
3
4
5
6
7
8
# free -m
total used free shared buff/cache available
Mem: 2004 351 596 205 1057 1222
Swap: 2036 0 2036
# free -g
total used free shared buff/cache available
Mem: 1 0 0 0 0 0
Swap: 1 0 1

3.4.2. uname 查看系统与内核相关信息

uname命令可以列出当前系统的内核版本、主要硬件平台以及CPU类型等信息。

使用方法:uname [-asrmpi]

参数 意义
-a 所有系统相关的信息,包括下面的数据都会被列出来
-s 系统内核名称
-r 内核的版本
-m 本系统的硬件名称,例如i686或x86_64等
-p CPU的类型,与-m类似,只是显示的是CPU的类型
-i 硬件的平台(ix86)

例子:输出系统的基本信息

1
2
[theshu@theshuhost ~]$ uname -a
Linux theshuhost 3.10.0-693.11.1.el7.x86_64 #1 SMP Mon Dec 4 23:52:40 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

另外还有两个查看系统的发行版本的命令:

  • cat /etc/issur
  • cat /etc/redhat-release

3.4.3. w 查看系统负载

Linux系统管理员最常用的命令就是这个w了。用来查看系统整体上的负载,通过查看这些信息就可以知道当前系统有没有压力。其具体用法如下:

1
2
3
4
# w
15:40:14 up 28 min, 1 user, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 192.168.1.105 15:25 6.00s 0.06s 0.01s w

关于w命令的说明:

  • 第一行从左到右一次显示的信息是:时间-系统运行时间-登陆用户数-平均负载
  • 从第二行开始的所有行则是:当前登陆的用户名,其登陆终端(tty表示本地终端,pts\0表示远程终端),IP地址、登陆时间、WHAT所使用的命令。
  • 在这些信息中,最应该关注的就是第一行中的load average:后面的三个数值了

关于load average的说明:

  • 第一个数值表示一分钟内系统的平均负载值,第二个数值表示五分钟内系统的平均负载值,第三个数值表示十五分钟内系统的平均负载值。
  • 我们着重看第一个值,它表示单位时间内使用CPU的活动进程数(在这里其实就是指一分钟内),该值越大说明服务器压力越大。一般情况下,这个值只要不超过服务器的CPU数量就没有关系。(如果服务器的CPU数量为8,那么值小于8就说明当前服务器没有压力,否则就需要关注一下了)

查看服务器有几个CPU的方法如下:

  • cat /proc/cpuinfo 查看/proc/cpuinfo这个文件的内容,该文件记录了CPU的详细信息。
  • 目前市面上的服务器有很多是2颗多核CPU,在Linux看来,它就是2*n个CPU(这里的n为单颗物理CPU上有几核)。
  • 假如n是4,则查看这个文件时会显示8段类似的信息,而最后一段信息的processor:后面会显示7。
  • 所以查看当前系统有几个CPU,可以用这个命令:grep -c 'processor' /proc/cpuinfo
  • 然而查看有几颗物理CPU时,则需要查看关键字”physical id”:grep -c 'physical' /proc/cpuinfo

补充内容:

  • ab -n 10000 -c 100 网址 用来对网站进行压力测试:访问10000次,并发量100.。
  • uptime 显示w命令的第一行内容

3.4.4. uptime 查看系统启动时间与工作负载

示例如下:

1
2
[root@theshuhost ~]# uptime
19:40:20 up 24 days, 22:31, 1 user, load average: 0.00, 0.01, 0.05

上例所示,uptime命令所展示的内容可简单地查看到系统负载。

3.4.5. netstat 跟踪网络

命令作用:netstat命令的作用是显示IP、TCP、UDP、ICMP等协议相关的统计信息和当前的TCP/IP网络连接状况。这个命令比较常用在网络的监控方面,不过,在进程管理方面也是需要了解的。加入系统里面没有这个命令,则需要先安装这个yum install -y net-tools。这个命令的使用方法如下:

netstat 参数

参数 意义
-a或–all 将目前系统上所有的连接、监听、Socket数据都列出来
-A [网络类型] 列出该网络类型连线中的相关地址。网络类型可以选择inet、unix、ipx、ax25、netrom和ddp
–网络类型 同上
-e 或 –extend 显示网络其他相关信息
-g 或 –groups 显示多重广播功能群组组员名单
-h 或 –help 在线帮助
-i 或 –interfaces 显示指定网络接口的所有信息
-l 或 –listening 显示监控中的服务器的Socket
-n 或 –numeric 不列出进程的服务名称,以端口号(port number)来显示
-o 或 –timers 显示计时器
-r 或 –route 显示内核路由表信息
-s 或 –statistice 显示个网络协议的统计信息
-t 或 –tcp 列出 tcp 网络数据包的数据
-u 或 –udp 列出 udp 网络数据包的数据
-v 或 –verbose 显示命令执行过程
-V 或 –version 显示版本信息
-p 列出该网络服务的进程PID

命令示例如下:

  • 查看本机的网络连接状况以及各协议的相关统计信息:netstat
  • 查看本机内核路由表信息:netstat -nr
  • 查看本机网络接口的当前配置信息:netstat -i
  • 查看本机TCP传输协议的连接状况:netstat -ta
  • 每隔10秒显示一次活动的TCP连接的连线状况:netstat -o 10 -t
  • 显示以太网网络接口的统计信息和所有协议的统计信息:netstat -es
  • 查看监听中的服务器套接字:netstat -l
  • 查看多播组成员信息:netstat -g

示例如下:列出目前系统已经新建的网络连接与 unix socket状态

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@theshuhost ~]# netstat
Active Internet connections (w/o servers) 《==与网络相关的部分
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 64 theshuhost:ssh 1.31.240.177:6150 ESTABLISHED
tcp 0 0 theshuhost:49254 106.11.68.13:http ESTABLISHED
Active UNIX domain sockets (w/o servers) 《==与本机的进程自己的相关性(非网络)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ] DGRAM 9655 /run/systemd/shutdownd
unix 2 [ ] DGRAM 6864 /run/systemd/notify
unix 2 [ ] DGRAM 6866 /run/systemd/cgroups-agent
unix 5 [ ] DGRAM 6877 /run/systemd/journal/socket
unix 10 [ ] DGRAM 6879 /dev/log
……(中间省略)……

如上,netstat的输出分为两大部分,分别是网络与系统自己的进程相关性部分:

  • 与网络相关的部分:
    • Proto:网络的数据包协议,主要分为TCP与UDP数据包
    • Recv-Q:非由用户进程连接到此socket的复制的总字节数
    • Send-Q:非由远程主机传送过来的acknowledge总字节数
    • Local Address:本地的IP端口情况
    • Foreign Adress:远程主机的IP端口情况
    • State:连接状态,主要有建立(ESTABLISED)及监听(LISTEN)
  • 除了网络上的连接之外,其实Linux系统上面的进程可以接收不通进程所发送来的信息,那就是Linux上面的socket file。socket file 可以沟通两个进程之间的信息,因此进程可以取得对方传送过来的数据。上面的socket file的输出字段有:
    • Proto:一般就是unix
    • RefCnt:;连接到此 socket 的进程数量
    • Flags:连接的标识
    • Type:socket访问的类型。主要有确认连接的STREAM与不需确认的DGRAM两种
    • State:若为CONNECTED表示多个进程之间已经建立连接
    • Path:连接到此socket的相关程序的路径,或者是相关数据输出的路径

netstat命令用来打印网络连接状况、系统所开放的端口、路由表等信息。关于这个命令最常用的两种方式就是netstat -lnp(打印当前系统启动哪些端口)和netstat -an(打印网络连接状况),请一定要记住。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# netstat -lnp
(并非所有进程都能被检测到,所有非本用户的进程信息将不会显示,如果想看到所有信息,则必须切换到 root 用户)
激活Internet连接 (仅服务器)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:3689 0.0.0.0:* LISTEN 4186/rhythmbox
tcp 0 0 127.0.1.1:53 0.0.0.0:* LISTEN -
tcp6 0 0 :::3689 :::* LISTEN 4186/rhythmbox
udp 0 0 127.0.1.1:53 0.0.0.0:* -
udp 0 0 0.0.0.0:68 0.0.0.0:* -
udp 0 0 0.0.0.0:37475 0.0.0.0:* -
udp 0 0 0.0.0.0:631 0.0.0.0:* -
udp 0 0 192.168.43.247:123 0.0.0.0:* -
udp 0 0 127.0.0.1:123 0.0.0.0:* -
udp 0 0 0.0.0.0:123 0.0.0.0:* -
udp 0 0 0.0.0.0:5353 0.0.0.0:* -
udp6 0 0 :::50784 :::* -
udp6 0 0 fe80::3e92:1783:1f3:123 :::* -
udp6 0 0 ::1:123 :::* -
udp6 0 0 :::123 :::* -
udp6 0 0 :::5353 :::* -
raw6 0 0 :::58 :::* 7 -
活跃的UNIX域套接字 (仅服务器)
Proto RefCnt Flags Type State I-Node PID/Program name 路径
unix 2 [ ACC ] 流 LISTENING 23756 1632/systemd /run/user/1000/systemd/private
unix 2 [ ACC ] SEQPACKET LISTENING 1896 - /run/udev/control
unix 2 [ ACC ] 流 LISTENING 22839 - /run/user/1000/keyring/control
.......
# netstat -an
激活Internet连接 (服务器和已建立连接的)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:3689 0.0.0.0:* LISTEN
tcp 0 0 127.0.1.1:53 0.0.0.0:* LISTEN
tcp 0 0 192.168.43.247:46792 123.58.182.252:80 ESTABLISHED
tcp6 0 0 :::3689 :::* LISTEN
udp 0 0 127.0.1.1:53 0.0.0.0:*
udp 0 0 0.0.0.0:68 0.0.0.0:*
udp 0 0 0.0.0.0:37475 0.0.0.0:*
udp 0 0 0.0.0.0:631 0.0.0.0:*
udp 0 0 192.168.43.247:123 0.0.0.0:*
udp 0 0 127.0.0.1:123 0.0.0.0:*
udp 0 0 0.0.0.0:123 0.0.0.0:*
udp 0 0 0.0.0.0:5353 0.0.0.0:*
udp6 0 0 :::50784 :::*
udp6 0 0 fe80::3e92:1783:1f3:123 :::*
udp6 0 0 ::1:123 :::*
udp6 0 0 :::123 :::*
udp6 0 0 :::5353 :::*
raw6 0 0 :::58 :::* 7
活跃的UNIX域套接字 (服务器和已建立连接的)
Proto RefCnt Flags Type State I-Node 路径
unix 2 [ ] 数据报 23755 /run/user/1000/systemd/notify
unix 2 [ ACC ] 流 LISTENING 23756 /run/user/1000/systemd/private
unix 2 [ ACC ] SEQPACKET LISTENING 1896 /run/udev/control
unix 2 [ ACC ] 流 LISTENING 22839 /run/user/1000/keyring/control
unix 2 [ ACC ] 流 LISTENING 24162 /run/user/1000/keyring/ssh
......

关于netstat -an命令内容中连接状态的说明:

  • TIME_WAIT 等待
  • ESTALISHED 已连接
  • LISHEN 监听
  • netstat -an | wc -l 可简单查看一下机器的并发量
  • 如果你所管理的服务器是一台提供Web服务(80端口)的服务器,那么就可以使用netstat -an | grep 80来查看当前连接Web服务的有哪些IP了

3.4.6. dmesg 分析内核产生的信息

系统在开机的时候,内核会去检测系统的硬件,你的某些硬件到底有没有被识别出来,那就与这个时候的检测有关。但是这些检测的过程要不是没有显示在屏幕上,就是很飞快地在屏幕上一闪而过。能不能把内核检测的信息找出来看看?当然是可以的,就是用dmesg这个命令。

所有内核检测的信息,不管是开机时候还是系统运行过程中,反正只要是内核产生的信息都会被记录到内存中的某个保护区段。dmesg这个命令就能够将该区段的信息读出来。因为信息实在太多了,所以执行时可以加入管道命令| more| less来方便阅读。

示例如下:

1
2
3
4
5
6
7
8
9
10
例一:输出所有的内核开机时的信息
[root@theshuhost ~]# dmesg | less
---
例二:查找开机的时候硬盘的相关信息
[root@theshuhost ~]# dmesg | grep -i vd
[ 1.088226] ata2.00: ATAPI: QEMU DVD-ROM, 2.1.2, max UDMA/100
[ 1.088921] scsi 1:0:0:0: CD-ROM QEMU QEMU DVD-ROM 2.1. PQ: 0 ANSI: 5
[ 1.185527] vda: vda1
[ 1.286350] EXT4-fs (vda1): mounted filesystem with ordered data mode. Opts: (null)
[ 2.591818] EXT4-fs (vda1): re-mounted. Opts: (null)

由例二就可以得知该Linux主机的硬盘的格式是什么了。如果是查找网卡的信息,可以试一试:dmesg | grep -i eth

3.4.7. vmstat 检测系统资源变化

命令w查看的是系统整体上的负载,通过查看那个数值就可以知道当前系统有没有压力,但它无法判断究竟具体是哪里(CPU、内存、磁盘等)有压力。所以这就用到了vmstat这个命令。

使用方式:

  • vmstat [-a] [延迟 【总计检测次数] CPU/内存等信息
  • vmstat [-fs] 内存相关
  • vmstat [-S 单位] 设置显示数据的单位
  • vmstat [-d] 与磁盘有关
  • vmstat [-p 分区] 与磁盘有关
参数 意义
-a 使用 inactive/active(活跃与否)代替 buffer/cache 的内存输出信息
-f 开机到目前为止系统复制(fork)的进程数
-s 将一些事件(开机至目前为止)导致的内存变化情况列表说明
-S 后面可以接单位,让显示的数据有单位,例如:K/M 取代bytes
-d 列出磁盘的读写总量统计表
-p 后列接分区名,可显示该分区的读写总量统计表

其常用的用法如下:

1
2
3
4
# vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 522200 2076 349976 0 0 30 34 51 47 0 0 99 0 0

vmstat命令打印的结果共分为6个部分:procs、memory、swap、io、system、cpu。请重点关注一下r、b、si、so、bi、bo这几列信息。

说明:

  • procs显示进程的相关信息
    • r(run):表示运行或等待CPU时间片的进程数。不要误认为等待CPU时间片意味着这个进程没有运行,实际上某一时刻1个CPU只能有一个进程占用,其他进程只能排着队等着,此时这些排队等待CPU资源的进程依然是运行状态。该数值如果长期大于服务器CPU的个数,则说明CPU的资源不够用了。
    • b(block):表示等待资源的进程数,这个资源指的是I/O、内存等。举个例子,当磁盘读写非常频繁时,写数据就会非常慢,此时CPU运算很快就结束了,但进程需要把计算的结果写入磁盘,这样进程的任务才算完成,那此时这个进程只能慢慢地等待磁盘了,这样这个进程就是这个状态。该数值如果长时间大于1,则需要关注一下了。
  • memory显示内存的相关信息
    • swap:表示切换到交换分区中的内存数量,单位kb。
    • free:表示当前空闲的内存数量,单位kb。
    • buff:表示(即将写入磁盘的)缓冲大小,单位kb。
    • cache:表示(从磁盘中读取的)缓存大小,单位kb。
  • swap显示内存的交换情况
    • si:表示由交换区写入内存的数据量,单位kb。
    • so:表示由内存写入交换区的数据量,单位kb。
  • io显示磁盘的使用情况
    • bi:表示从块设备读取数据的量(读磁盘),单位kb。
    • bo:表示从块设备写入数据的量(写磁盘),单位kb。
  • system显示采集间隔内发生的中断次数
    • in:表示再某一段时间间隔内观测到的每秒设备的中断次数。
    • cs:表示每秒产生的上下文切换次数。
  • cpu显示CPU的使用状态
    • us:显示用户下所花费CPU的时间百分比。
    • sy:显示系统花费CPU的时间百分比。
    • id:表示CPU处于空闲状态的时间百分比。
    • wa:表示I/O等待所占用CPU的时间百分比。
    • st:表示被偷走的CPU所占百分比(一般都为0,不用关注)

以上所介绍的各个参数中,再运维工作中经常会关注r、b、wa这三列。io部分的bi和bo也是要经常参考的对象,如果磁盘io压力大,这两列的数值就会比较高。另外,当si和so两列的数值比较高并且不断变化时,说明内存不够了,内存中的数据会频繁交换到交换分区中,这往往对系统性能影响极大。

我们使用vmstat查看系统状态时,通常都是使用如下形式:

  • vmstat 1 5 表示每隔1秒输出一次状态,共输出5次。
  • cmstat 1 表示每隔1秒输出一次状态且一直输出,知道按下Ctrl+C结束。

4. 几个其它的运维工具

在这一节包含了除了上一节所介绍的工具之外的其它几个常用的工具。它们都是在运维工作中经常使用到的,所以要必须掌握。

4.1. 监控系统状态

4.1.1. sar 监控系统状态

sar命令很强大,它可以监控系统几乎所有资源的状态,比如平均负载、网卡流量、磁盘状态、内存使用等。与其它系统状态监控工具不同,它可以打印历史信息,可以显示当天从零点开始到当前时刻的系统状态信息。如果你的系统没有安装这个命令,请使用yum install -y sysstat安装。

初次使用sar命令会报错,这是因为sar工具还没有生成相应的数据库文件(无需实时监控,因为不用去查询那个库文件)。它的数据库文件再/var/log/sa/目录下。因为这个命令太复杂,下面仅仅介绍最常用的两个方面的用法。

用法1. 查看网卡流量 sar -n DEV

具体用法如下:

1
2
3
4
5
6
7
8
9
10
# sar -n DEV
Linux 2.6.32-358.el6.i686 (localhost.localdomain) 20130525日 _i686_ (1 CPU)
000001秒 IFACE rxpck/s txpck/s rxKB/s txKB/s rxcmp/s txcmp/s rxmcst/s
001001秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00
001001秒 eth0 31.94 0.09 3.89 0.02 0.00 0.00 0.00
002001秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00
002001秒 eth0 32.40 0.04 3.96 0.01 0.00 0.00 0.00
003001秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00
003001秒 eth0 31.18 0.09 3.76 0.02 0.00 0.00 0.00

说明:

  • 在上面所示的例子里并没有把全部信息贴出来,因为太多了。
  • IFACE这列表示设备名称。
  • rxpck/s表示每秒进入收取的包的数量(单位Byte)。
  • txbyt/s表示每秒发送的数据量。
  • 后面几列不需要关注。
  • 如果有一天您所管理的服务器丢包非常严重,那么您就应该看一看这个网卡流量是否异常了,如果rxpck/s 那一列的数值大于4000,或者rxbyt/s那列大于5,000,000,则很有可能是被攻击了,正常的服务器网卡流量不会高于这么多,除非是您自己在拷贝数据。

上面的命令是查看网卡流量历史的,如何实时查看网卡流量呢?用下面的命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ sar -n DEV 1 5
Linux 4.4.0-53-generic (theshu-Latitude-D530) 20171006日 _i686_ (2 CPU)
113305秒 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
113306秒 wlp12s0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113306秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113306秒 enp9s0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113306秒 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
113307秒 wlp12s0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113307秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113307秒 enp9s0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113307秒 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
113308秒 wlp12s0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113308秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113308秒 enp9s0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
c
113308秒 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
113309秒 wlp12s0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113309秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113309秒 enp9s0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113309秒 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
113310秒 wlp12s0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113310秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
113310秒 enp9s0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
平均时间: IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
平均时间: wlp12s0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
平均时间: lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
平均时间: enp9s0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00

上面的命令是很常用的,用来查看网卡流量,一秒钟显示1次,共显示5次。着重查看rxpck/s(数据包量)和rxbyt/s(数据量)

另外也可以查看某一天的网卡流量历史,使用-f选项,后面跟文件名,如果你的系统为CentOS或者是RedHat,那么sar的库文件一定是在/var/log/sa/目录下的:

1
2
3
4
5
6
7
8
9
10
11
12
# sar -n DEV -f /var/log/sa/sa24
Linux 2.6.32-358.el6.i686 (localhost.localdomain) 20130525日 _i686_ (1 CPU)
104936秒 LINUX RESTART
110001秒 IFACE rxpck/s txpck/s rxKB/s txKB/s rxcmp/s txcmp/s rxmcst/s
111001秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00
112001秒 eth0 31.94 0.09 3.89 0.02 0.00 0.00 0.00
113001秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00
114001秒 eth0 32.40 0.04 3.96 0.01 0.00 0.00 0.00
115001秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00
116001秒 eth0 31.18 0.09 3.76 0.02 0.00 0.00 0.00

ls /var/log/sa/ 你会看到两种不同的文件,一个是以sa开头加日期,一个是sar开头加日期,其中sa加日期的文件是不能直接用cat查看的,只能使用sar -f来查看,另外一种sar加日期的文件是可以直接用cat等工具查看的。他们都是记录系统状态历史信息。

用法2. 查看历史负载

示例如下:

1
2
3
4
5
6
7
8
9
10
11
# sar -q
Linux 2.6.32-358.el6.i686(localhost.localdomain) 20130525日 _i686_(1 CPU)
000001秒 runq-sz plist-sz ldavg-1 ldavg-5 ldavg-15
0010010 142 0.00 0.00 0.00
0020010 143 0.00 0.00 0.00
0030010 143 0.00 0.01 0.00
0040010 143 0.05 0.01 0.00
0050010 143 0.00 0.00 0.00
0100010 143 0.00 0.00 0.00
0110010 143 0.08 0.03 0.00

说明:

  • 要使用这个命令,前提是要开启服务/etc/init.d/sysstat服务。
  • 可以指定历史文件:sar -q -f /var/log/sa/sa10
  • 这个命令有助于我们查看服务器在过去的某个时间的负载状况。
  • 补充一个sar的用法:sar -b 用来查看磁盘的读写。

关于sar初学着掌握以上这两个用法即可。如果以后工作中用到,就man一下吧。

4.1.2. nload 查看网卡流量

sar命令虽然可以查看网卡流量,但是不够直观,还有一个更好的工具,那就是nload

使用方法:直接运行nload命令。然后会出现如下的动态界面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Device eth0 [172.17.97.250] (1/2):
================================================================================
Incoming:
Curr: 856.00 Bit/s
Avg: 1.05 kBit/s
Min: 856.00 Bit/s
Max: 2.52 kBit/s
Ttl: 83.53 MByte
Outgoing:
Curr: 8.08 kBit/s
Avg: 8.02 kBit/s
Min: 6.02 kBit/s
Max: 9.03 kBit/s
Ttl: 46.12 MByte

最上面一行为网卡名字以及IP地址,按向右箭头可以查看其它网卡的网络流量。输出结果分为两个部分,Incoming为进入网卡的流量,Outgoing为网卡出去的流量,我们关注的当然是Curr那行的数据,其单位也是可以动态调整的,非常人性化。按q退出该界面。

4.2. 抓包工具

接下来介绍的两个抓包工具tcpdump和tshark,都需要用root权限去执行。而且,作为运维人员,我们抓包主要是看数据的流向。

4.2.1. tcpdump工具

如果没有这个命令,需要用yum install -y tcpdump命令去安装一下。下面是关于这个命令的两个常用的方式:

  1. tcpdump -nn -i eth0
    • -i选项后面跟设备名称,如果想抓eth1网卡的包,后面则要跟eth1。至于-nn选项的作用则是让第三列和第四列显示成”IP+端口号”的形式,如果不加-nn则显示的是”主机名+服务名称”。
    • 这里需要关注的只是第三列和第四列,显示的信息为哪一个IP+Port在连接哪一个IP+Port。
  2. tcpdump -nn -i eth0 host 192.168;.1.100 and port 80 -c 100 -w 1.cap
    • 可以用host指定ip,用port指定端口,-c指定包数量,-w写入指定文件里。这样1.cap文件里面是包的内容,而如果不加-w则直接在屏幕上显示的不是数据包,而是数据流向。这个1.cap可以下载到Windows系统上,然后使用wireshark查看。
    • -s0选项是抓取全部包
    • 如果在网卡名后面加上tcp,则只会抓取tcp的包
    • and表示并且,not表示取反
  3. strings 可简单查看二进制的包

4.2.2. wireshark工具

默认CentOS是没有这个命令的,请使用这个命令来安装它yum install -y wireshark。关于这个工具只需要掌握一个用法即可,如下所示:

tshark -n -t a -R http.request -T fields -e "frame.time" -e "ip.src" -e "http.host" -e "http.request.uri"

说明:

  • 这条命令用于Web服务器,可以显示诸如下面的信息: Mar 21,2014 15:37:08.857507000 199.30.20.38 k168.123.com GET /thread-4861-1-1.html 。
  • 这类似于Web访问日志,有时候若是服务器没有配置访问日志,可以临时使用该命令查看一下当前服务器上的web请求。通俗的说,也就是显示访问http请求的域名以及url。
  • 这个命令,知道你Ctrl+C才会显示出结果

其它的用法:

  • 抓取MySQL的查询:
    • tshark -n -i eth0 -R 'mysql.query' -T fileds -e "ip.src" -e "mysql.query"
    • tshark -i eth0 port 3307 -d tcp.port==3307,mysql -z "proto,colinfo,mysql.query,mysql.query"
  • 抓取指定类型的MySQL查询:tshark -n -i eth0 -R 'mysql matches "SELECT|INSERT|DELETE|UPDATE"' -T fields -e "ip.src" -e "mysql.query"
  • 统计HTTP的状态:tshark -n -q -z http,stat, -z http,tree这个命令,知道你Ctrl+C之后才会显示结果

5. 特殊文件与程序

在这一节将介绍 SUID/SGIN/SBIT 这些权限对于程序到底是如何影响的,程序可能会使用到系统资源(举例来说,磁盘就是其中一项资源)。

5.1. 具有SUID/SGID权限的命令执行状态

SUID的权限其实与程序的相关性非常大。这是为什么呢?先来看看SUID的程序是如何被一般用户执行,且具有什么特色呢?

  • SUID权限仅对二进制程序(binary program)有效
  • 执行者对于该程序需要具有x的可执行权限
  • 本权限仅在执行程序的过程中有效(run-time)
  • 执行者将具有该程序所有者(owner)的权限

所以说,整个SUID的权限会生效是由于具有该权限的程序所触发,而我们知道一个程序被触发会变成进程。所以,执行者可以具有程序所有者的权限就是在该程序变成进程的那个时候。比如,passed这个命令,它被触发的时候,就会获取一个新的进程与PID,该PID产生时通过SUID来给予该PID特殊的权限设置。可以通过下面的例子来理解一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[theshu@theshuhost ~]$ passwd
更改用户 theshu 的密码 。
为 theshu 更改 STRESS 密码。
(当前)UNIX 密码: <==在这里按下Ctrl+Z,再按下Enter键
[1]+ 已停止 passwd
[theshu@theshuhost ~]$ pstree -u
systemd─┬─AliYunDun───14*[{AliYunDun}]
├─AliYunDunUpdate───3*[{AliYunDunUpdate}]
├─agetty
├─aliyun-service───6*[{aliyun-service}]
├─atd
├─auditd───{auditd}
├─crond
├─dbus-daemon(dbus)
├─dhclient
├─ntpd(ntp)
├─polkitd(polkitd)───5*[{polkitd}]
├─rsyslogd───2*[{rsyslogd}]
├─sshd───sshd───sshd(theshu)───bash─┬─passwd(root)
│ └─pstree
├─systemd-journal
├─systemd-logind
├─systemd-udevd
└─tuned───4*[{tuned}]

那么怎样查询整个系统的SUID/SGID的文件呢?使用find命令:find / -perm +6000

5.2. /proc/* 代表的意义

其实,我们之前提到的所谓进程都是在内存当中,而内存的当中的数据又是写入到/proc/这个目录下的,所以,我们可以直接查看/proc/这个目录当中的文件。查看如下:

1
2
3
4
5
6
7
8
9
10
11
12
[root@theshuhost ~]# ll /proc/
total 0
dr-xr-xr-x 9 root root 0 Dec 28 05:08 1
dr-xr-xr-x 9 root root 0 Dec 28 05:08 10
dr-xr-xr-x 9 root root 0 Dec 27 21:08 1148
dr-xr-xr-x 9 root root 0 Dec 28 05:08 12
....(中间省略).....
-r--r--r-- 1 root root 0 Jan 23 22:10 uptime
-r--r--r-- 1 root root 0 Jan 23 22:10 version
-r-------- 1 root root 0 Jan 23 22:10 vmallocinfo
-r--r--r-- 1 root root 0 Jan 23 22:10 vmstat
-r--r--r-- 1 root root 0 Jan 23 22:10 zoneinfo

基本上,目前主机上面的各个进程的PID都是以目录的类型存于/proc/目录当中的。举例来说,我们开机所执行的第一个进程systemd(低版本为init)的PID是1,这个PID的所有相关信息都写入在/proc/1/目录下面。我们来直接查看PID为1的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@theshuhost ~]# ll /proc/1/
total 0
dr-xr-xr-x 2 root root 0 Jan 23 22:16 attr
-rw-r--r-- 1 root root 0 Jan 23 22:16 autogroup
-r-------- 1 root root 0 Jan 23 22:16 auxv
-r--r--r-- 1 root root 0 Dec 28 05:08 cgroup
--w------- 1 root root 0 Jan 23 22:16 clear_refs
-r--r--r-- 1 root root 0 Dec 28 05:08 cmdline <==就是命令串
-rw-r--r-- 1 root root 0 Dec 28 05:08 comm
-rw-r--r-- 1 root root 0 Jan 23 22:16 coredump_filter
-r--r--r-- 1 root root 0 Jan 23 22:16 cpuset
lrwxrwxrwx 1 root root 0 Jan 23 22:16 cwd -> /
-r-------- 1 root root 0 Dec 28 05:08 environ <==一些环境变量
lrwxrwxrwx 1 root root 0 Dec 28 05:08 exe -> /usr/lib/systemd/systemd <==实际执行的命令
....(一下省略)....

里面的数据很多,不过,比较有趣的其实是这两个文件,分别是:

  • cmdline:这个进程被启动的命令串
  • environ:这个进程的环境变量内容

查阅一下cmdline这个文件,发现它的内容如下:

1
2
[root@theshuhost ~]# cat /proc/1/cmdline
/usr/lib/systemd/systemd--switched-root--system--deserialize21

就是这个命令、参数启动systemd的。这还是跟某个特定的PID有关的内容。如果是针对整个Linux系统相关的参数呢?那就是在/proc/目录下面的文件。相关文件与对应的内容如下表:

文件名 文件内容
/proc/cmdline 加载kernel时所执行的相关参数。查阅此文件,可了解系统是如何启动的
/proc/cpuinfo 本机的CPU的相关信息,包含频率、类型与运算功能等
/proc/devices 这个文件记录了系统各个主要设备的主要设备代号,与mknod有关
/proc/filesystems 目前系统已经加载的文件系统
/proc/interrupts 目前系统上面的IRQ分配状态
/proc/ioports 目前系统上面各个设备所配置的I/O地址
/proc/kcore 这个就是内存的大小。好大对吧?但是不要读它啦
/proc/loadavg top和uptime命令的上头的三个平均负载值就是记录在这里的
/proc/meminfo 使用free列出的内存信息,在这里也能够查阅到
/proc/modules 目前我们的Linux已经加载的模块列表,也可以想成是驱动程序
/proc/mounts 系统已经挂载的数据,就是用mount这个民工调出来的数据
/proc/swaps 到底系统加载的内存在哪里?使用的分区记录在此
/proc/partitions 使用fdisk -l会出现目前所有的分区吧,在这个文件当中也有记录
/proc/pci 在PCI总线上面每个设备的详细情况,可用lspci来查阅
/proc/uptime 就是用uptime的时候会出现的信息
/proc/version 内核的版本,就是用uname -a显示的内容
/proc/bus/* 一些总线的设备,还有USB的设备也记录在此

其实,上面的这些文件都可以使用cat等命令来查阅,不必深入了解。不过,查看文件内容后,毕竟会比较有感觉。如果将来你想要自行编写某些工具软件,那么这个目录下的相关文件可能会对你有点帮助。

5.3. 查询已打开文件或已执行程序打开的文件

还有一些与程序相关的命令可以值得参考与应用。如下。

5.3.1. fuser 通过文件(或文件系统)找出正在使用该文件的程序

有时候我想要知道我的进程到底在这次启动过程中打开了多少文件,可以利用fuser来查看。举例来说,你如果卸除时发现系统通知“device is busy”,那表示这个文件系统正在忙碌中,表示有某个进程有利用该文件系统。那么你就可以利用fuser来跟踪。fuser语法优点像这样:

fuser [-umv] [-k [i] [-signal]] file/dir

参数 意义
-u 除了进程的PID之外,同时列出该进程的所有者
-m 后面接的哪个文件名会主动上提到该文件系统的所顶层,对 umount 不成功很有效
-v 可以列出每个文件与程序还有命令的完整相关性
-k 找出使用该文件或目录的PID,并试图以SIGKILLz这个信号给予该PID
-i 必须与-k配合,在删除PID之前先询问用户意愿
-signal 例如 -1 -15 等,若不加的话,默认是 SIGKILL(-9)

范例一:找出目前所在目录的使用 PID/所属账号/权限

1
2
3
[root@theshuhost ~]# fuser -uv .
USER PID ACCESS COMMAND
/root: root 11086 ..c.. (root)bash

说明:

  • 观察输出结果,它说’.’下面有个PID为11086的程序,该程序属于root并且命令为bash。
  • 比较有趣的是哪个ACCESS的选项,那个选项代表的意义为:
    • c:此进程在当前的目录下(非子目录)
    • e:可被触发为执行状态
    • f:是一个被打开的文件
    • r:代表顶层目录(root directory)
    • F:该文件被打开了,不过在等待回应中
    • m:可能为分享的动态函数库

那如果你想要查看某个文件系统下面有多少进程正在占用该文件系统时,那个-m的参数就很有帮助了。请看下面的例子。

范例二:查看一下/proc/的文件系统有多少进程正在利用它

1
2
3
4
5
6
7
8
9
10
[root@theshuhost ~]# fuser -uv /proc
USER PID ACCESS COMMAND
/proc: root kernel mount (root)/proc
其实我们需要用到的是/proc下面的文件,所以应该要这样做:
[root@theshuhost ~]# fuser -muv /proc
USER PID ACCESS COMMAND
/proc: root kernel mount (root)/proc
root 1 f.... (root)systemd
root 326 f.... (root)systemd-journal
这样就可以看到有几个进程在进行/proc文件系统的访问。

既然可以针对整个文件系统,那么能不能仅针对单一文件呢?当然可以,请看下面的例子。

范例三:找到 /var/ 下面属于 FIFO 类型的文件,并且找出访问该文件的进程

1
2
3
4
5
6
7
[root@www ~]# find /var -type p
/var/gdm/.gdmfifo <==我们针对这个玩意即可!
/var/run/autofs.fifo-misc
/var/run/autofs.fifo-net
[root@www ~]# fuser -uv /var/gdm/.gdmfifo
USER PID ACCESS COMMAND
/var/gdm/.gdmfifo: root 4892 F.... (root)gdm-binary

范例四:同范例三,但试图删除该PID且“不要”删除

1
2
3
[root@www ~]# fuser -ki /var/gdm/.gdmfifo
/var/gdm/.gdmfifo: 4892
Kill process 4892 ? (y/N) n

看吧,通过这个fuser我们可以找出使用该文件、目录的程序,以便查看。它的重点与ps,pstree不同。fuser可以让我们了解到某个文件(或文件系统)目前正在被哪些进程所利用。

5.3.2. lsof:列出被进程所打开的文件名

相对于fuser是由文件或者设备去找出使用该文件或设备的进程,反过来说,如何查出某个进程打开或者使用的文件与设备呢?那就是使用lsof。

lsof [-aUu] [+d]

参数 意义
-a 多项数据需要“同时成立”才显示出结果
-U 仅列出Unix like 系统的socket文件类型
-u 后面接username,列出该用户相关进程所打开的文件
+d 后面接目录,即找出某个目录下面已经被打开的文件

示例如下:

范例一:列出目前系统上面所有已经被打开的文件与设备

1
2
3
4
5
6
7
8
9
[root@theshuhost ~]# lsof
COMMAND PID TID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root cwd DIR 253,1 4096 2 /
systemd 1 root rtd DIR 253,1 4096 2 /
systemd 1 root txt REG 253,1 1523624 1053844 /usr/lib/systemd/systemd
systemd 1 root mem REG 253,1 20040 1050451 /usr/lib64/libuuid.so.1.3.0
systemd 1 root mem REG 253,1 261336 1051898 /usr/lib64/libblkid.so.1.1.0
systemd 1 root mem REG 253,1 90664 1050435 /usr/lib64/libz.so.1.2.7
....下面省略....

说明:

  • 注意,在默认的情况下,lsof会将目前系统上面已经打开的文件全部列出来。所以,界面多得吓人啊!
  • 你可以注意到,第一个文件systemd执行的地方就在根目录,而根目录所在的inode也有显示出来。

范例二:仅列出关于root的所有进程打开的socket文件

1
2
3
4
5
6
7
8
9
10
11
[root@theshuhost ~]# lsof -u root -a -U
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root 12u unix 0xffff880036a09800 0t0 9607 /run/systemd/private
systemd 1 root 14u unix 0xffff88003ca1a000 0t0 389584 socket
systemd 1 root 23u unix 0xffff8800368fa800 0t0 6864 /run/systemd/notify
systemd 1 root 27u unix 0xffff8800368fb000 0t0 6866 /run/systemd/cgroups-agent
systemd 1 root 36u unix 0xffff88003617ac00 0t0 11251 socket
systemd 1 root 37u unix 0xffff88003db9f000 0t0 6874 /run/systemd/journal/stdout
systemd 1 root 38u unix 0xffff8800369b4000 0t0 6877 /run/systemd/journal/socket
systemd 1 root 39u unix 0xffff8800369b4400 0t0 6879 /dev/log
....下面省略....

说明:

  • 注意到那个-a选项了把,如果你分别输入lsof -u root 及 lsof -U,会有什么信息?
  • 使用 lsof -u root -U 及 lsof -u root -a -U,呵呵,都不同的。
  • -a 的用途就是解决同时需要两个选项都成立。

范例三:请列出目前系统上面所有的被启动的周边设备

1
2
3
4
5
6
7
8
9
[root@theshuhost ~]# lsof +d /dev
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root 0u CHR 1,3 0t0 4856 /dev/null
systemd 1 root 1u CHR 1,3 0t0 4856 /dev/null
systemd 1 root 2u CHR 1,3 0t0 4856 /dev/null
systemd 1 root 21r CHR 10,235 0t0 6516 /dev/autofs
systemd 1 root 39u unix 0xffff8800369b4400 0t0 6879 /dev/log
kdevtmpfs 12 root cwd DIR 0,5 3060 3 /dev
....下面省略....

说明:

  • 因为设备都在/dev/里面,所以查找目录即可。

范例四:显示出属于root的bash这个进程所打开的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@theshuhost ~]# lsof -u root | grep bash
bash 11086 root cwd DIR 253,1 4096 131073 /root
bash 11086 root rtd DIR 253,1 4096 2 /
bash 11086 root txt REG 253,1 960608 1050319 /usr/bin/bash
bash 11086 root mem REG 253,1 106070960 1058409 /usr/lib/locale/locale-archive
bash 11086 root mem REG 253,1 62184 1050006 /usr/lib64/libnss_files-2.17.so
bash 11086 root mem REG 253,1 2127336 1049988 /usr/lib64/libc-2.17.so
bash 11086 root mem REG 253,1 19776 1049994 /usr/lib64/libdl-2.17.so
bash 11086 root mem REG 253,1 174576 1050317 /usr/lib64/libtinfo.so.5.9
bash 11086 root mem REG 253,1 164112 1049977 /usr/lib64/ld-2.17.so
bash 11086 root mem REG 253,1 26254 1050290 /usr/lib64/gconv/gconv-modules.cache
bash 11086 root 0u CHR 136,0 0t0 3 /dev/pts/0
bash 11086 root 1u CHR 136,0 0t0 3 /dev/pts/0
bash 11086 root 2u CHR 136,0 0t0 3 /dev/pts/0
bash 11086 root 255u CHR 136,0 0t0 3 /dev/pts/0

这个命令可以找出你想要知道的某个进程是否有打开哪些信息,例如上面的范例四的执行结果。


OK

0%